package com.hwtx.form.domain.handle;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import org.apache.commons.compress.utils.Lists;

import java.util.*;

import static com.hwtx.form.domain.FormConstants.AUTH_KEY;
import static com.hwtx.form.domain.FormConstants.AUTH_NONE;
import static com.hwtx.form.util.JacksonUtil.*;

public class FormPermissionFilter {

    public static final String formElement = "form";
    public static final String dialogElement = "dialog";
    public static final String quickEditElement = "quickEdit";

    public static void permissionFilter(JsonNode root, List<String> resourceCodes) {

        FindNodeRet findNodeRet = new FindNodeRet();
        visitElements(root, AUTH_KEY, resourceCodes, new FindNodeCallback() {
            @Override
            public void handleObjectNode(JsonNode root, ObjectNode node, JsonNode parent, String nodeName) {
                boolean isAuth = isAuth(node, resourceCodes);
                String nodeType = node.get("type") != null ? node.get("type").asText() : null;
                if (Objects.equals(dialogElement, nodeName) && !isAuth) {
                    findNodeRet.getObjectNodes().add(FindNodeRet.FindObjectNode.builder().nodeName(nodeName).node(node).build());
                }
                if (Objects.equals(formElement, nodeType) && !isAuth) {
                    findNodeRet.getObjectNodes().add(FindNodeRet.FindObjectNode.builder().nodeName(nodeType).node(node).build());
                }
                if (node.has(quickEditElement) && !isAuth) {
                    findNodeRet.getObjectNodes().add(FindNodeRet.FindObjectNode.builder().nodeName("quickEdit").node(node).build());
                }
                if (parent != null && parent.isObject() && !isAuth) {
                    findNodeRet.getObjectNodes().add(FindNodeRet.FindObjectNode.builder().parent(parent).node(node).nodeName(nodeName).build());
                }
            }

            @Override
            public void handleArrayNode(JsonNode root, ArrayNode parent, JsonNode node, int index) {
                boolean isAuth = isAuth((ObjectNode) node, resourceCodes);
                boolean quickEdit = node.has("quickEdit");
                if (!isAuth && !quickEdit) {
                    findNodeRet.getArrayNodes().compute(parent, (k, v) -> {
                        if (v == null) {
                            v = new HashSet<>();
                        }
                        v.add(index);
                        return v;
                    });
                }
            }
        });
        handleArray(findNodeRet);
        handleObjectNode(findNodeRet);
    }

    private static void handleObjectNode(FindNodeRet findNodeRet) {
        findNodeRet.getObjectNodes().forEach(node -> {
            switch (node.getNodeName()) {
                case dialogElement -> handleDialog(node.getNode());
                case formElement -> handleForm(node.getNode());
                case quickEditElement -> node.getNode().remove("quickEdit");
                case null, default -> {
                    if (node.getParent() != null && node.getParent().isObject()) {
                        ((ObjectNode) node.getParent()).remove(node.getNodeName());
                    } else {
                        throw new UnsupportedOperationException("未知节点类型");
                    }
                }
            }
        });
    }

    private static void handleDialog(ObjectNode node) {
        ObjectNode button = createObjectNode();
        button.put("type", "button");
        button.put("actionType", "close");
        button.put("label", "返回");
        button.put("primary", true);
        ArrayNode actions = createArrayNode();
        actions.add(button);
        node.set("actions", actions);
    }

    private static void handleArray(FindNodeRet findNodeRet) {
        findNodeRet.getArrayNodes().forEach((k, v) -> v.stream().sorted(Comparator.comparingInt(Integer::intValue).reversed()).forEach(k::remove));
    }

    private static void handleForm(ObjectNode node) {
        node.put("static", "true");
    }

    private static boolean isAuth(ObjectNode node, List<String> resourceCodes) {
        boolean isAuth = false;
        if (node.get(AUTH_KEY) != null) {
            if (Objects.equals(node.get(AUTH_KEY).asText(), AUTH_NONE)) {
                isAuth = true;
            } else {
                isAuth = resourceCodes.contains(node.get(AUTH_KEY).asText());
            }
        }
        return isAuth;
    }

    @Data
    static class FindNodeRet {
        List<FindObjectNode> objectNodes = Lists.newArrayList();
        Map<ArrayNode, Set<Integer>> arrayNodes = new HashMap<>();

        @Getter
        @Builder
        static class FindObjectNode {
            String nodeName;
            ObjectNode node;
            JsonNode parent;
        }
    }
}
